﻿#region NeoPatterns
// 
// NeoPatterns framework is released under LGPLv3 license.
// See http://www.opensource.org/licenses/lgpl-3.0.html for more details.
// 
// Please visit http://neopatterns.wikidot.com for more information about NeoPatterns
// 
// Copyright (c) Pascal Craponne 2008
// 
#endregion

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace NeoPatterns.Utility
{
    /// <summary>
    /// Extensions to ILGenerator
    /// </summary>
    public static class ILGeneratorExtension
    {
        /// <summary>
        /// Emits an ldarg instruction
        /// </summary>
        /// <param name="ilGenerator"></param>
        /// <param name="index">argument index</param>
        public static void EmitLdarg(this ILGenerator ilGenerator, int index)
        {
            switch (index)
            {
            case 0:
                ilGenerator.Emit(OpCodes.Ldarg_0);
                break;
            case 1:
                ilGenerator.Emit(OpCodes.Ldarg_1);
                break;
            case 2:
                ilGenerator.Emit(OpCodes.Ldarg_2);
                break;
            case 3:
                ilGenerator.Emit(OpCodes.Ldarg_3);
                break;
            default:
                ilGenerator.Emit(OpCodes.Ldarg_S, (short)index);
                break;
            }
        }

        public static void EmitLdloc(this ILGenerator ilGenerator, LocalVariableInfo localVariableInfo)
        {
            switch (localVariableInfo.LocalIndex)
            {
            case 0:
                ilGenerator.Emit(OpCodes.Ldloc_0);
                break;
            case 1:
                ilGenerator.Emit(OpCodes.Ldloc_1);
                break;
            case 2:
                ilGenerator.Emit(OpCodes.Ldloc_2);
                break;
            case 3:
                ilGenerator.Emit(OpCodes.Ldloc_3);
                break;
            default:
                ilGenerator.Emit(OpCodes.Ldloc_S, (short)localVariableInfo.LocalIndex);
                break;
            }
        }

        public static void EmitStloc(this ILGenerator ilGenerator, LocalVariableInfo localVariableInfo)
        {
            switch (localVariableInfo.LocalIndex)
            {
            case 0:
                ilGenerator.Emit(OpCodes.Stloc_0);
                break;
            case 1:
                ilGenerator.Emit(OpCodes.Stloc_1);
                break;
            case 2:
                ilGenerator.Emit(OpCodes.Stloc_2);
                break;
            case 3:
                ilGenerator.Emit(OpCodes.Stloc_3);
                break;
            default:
                ilGenerator.Emit(OpCodes.Stloc_S, (short)localVariableInfo.LocalIndex);
                break;
            }
        }

        public static void EmitLdc(this ILGenerator ilGenerator, int value)
        {
            switch (value)
            {
            case 0:
                ilGenerator.Emit(OpCodes.Ldc_I4_0);
                break;
            case 1:
                ilGenerator.Emit(OpCodes.Ldc_I4_1);
                break;
            case 2:
                ilGenerator.Emit(OpCodes.Ldc_I4_2);
                break;
            case 3:
                ilGenerator.Emit(OpCodes.Ldc_I4_3);
                break;
            case 4:
                ilGenerator.Emit(OpCodes.Ldc_I4_4);
                break;
            case 5:
                ilGenerator.Emit(OpCodes.Ldc_I4_5);
                break;
            case 6:
                ilGenerator.Emit(OpCodes.Ldc_I4_6);
                break;
            case 7:
                ilGenerator.Emit(OpCodes.Ldc_I4_7);
                break;
            case 8:
                ilGenerator.Emit(OpCodes.Ldc_I4_8);
                break;
            default:
                if (value < 128)
                    ilGenerator.Emit(OpCodes.Ldc_I4_S, (byte)value);
                else
                    ilGenerator.Emit(OpCodes.Ldc_I4, value);
                break;
            }
        }

        public static void EmitUnboxOrCastIfNecessary(this ILGenerator ilGenerator, Type targetType)
        {
            if (targetType.IsValueType)
                ilGenerator.Emit(OpCodes.Unbox_Any, targetType);
            else if (targetType != typeof(object))
                ilGenerator.Emit(OpCodes.Castclass, targetType);
        }

        public static void EmitBoxIfNecessary(this ILGenerator ilGenerator, Type targetType)
        {
            if (targetType.IsValueType)
                ilGenerator.Emit(OpCodes.Box, targetType);
        }

        public static void EmitLdind(this ILGenerator ilGenerator, Type type)
        {
            if (type.IsValueType)
            {
                if (type == typeof(Byte))
                {
                    ilGenerator.Emit(OpCodes.Ldind_U1);
                    return;
                }
                if (type == typeof(SByte))
                {
                    ilGenerator.Emit(OpCodes.Ldind_I1);
                    return;
                }
                if (type == typeof(Int16))
                {
                    ilGenerator.Emit(OpCodes.Ldind_I2);
                    return;
                }
                if (type == typeof(UInt16))
                {
                    ilGenerator.Emit(OpCodes.Ldind_U2);
                    return;
                }
                if (type == typeof(Int32))
                {
                    ilGenerator.Emit(OpCodes.Ldind_I4);
                    return;
                }
                if (type == typeof(UInt32))
                {
                    ilGenerator.Emit(OpCodes.Ldind_U4);
                    return;
                }
                if (type == typeof(Int64) || type == typeof(UInt64))
                {
                    ilGenerator.Emit(OpCodes.Ldind_I8);
                    return;
                }
                if (type == typeof(Single))
                {
                    ilGenerator.Emit(OpCodes.Ldind_R4);
                    return;
                }
                if (type == typeof(Double))
                {
                    ilGenerator.Emit(OpCodes.Ldind_R8);
                    return;
                }
                ilGenerator.Emit(OpCodes.Ldobj, type);
            }
            else
            {
                ilGenerator.Emit(OpCodes.Ldind_Ref);
            }
        }

        public static void EmitStind(this ILGenerator ilGenerator, Type type)
        {
            if (type.IsValueType)
            {
                if (type == typeof(Byte) || type == typeof(SByte))
                {
                    ilGenerator.Emit(OpCodes.Stind_I1);
                    return;
                }
                if (type == typeof(Int16) || type == typeof(UInt16))
                {
                    ilGenerator.Emit(OpCodes.Stind_I2);
                    return;
                }
                if (type == typeof(Int32) || type == typeof(UInt32))
                {
                    ilGenerator.Emit(OpCodes.Stind_I4);
                    return;
                }
                if (type == typeof(Int64) || type == typeof(UInt64))
                {
                    ilGenerator.Emit(OpCodes.Stind_I8);
                    return;
                }
                if (type == typeof(Single))
                {
                    ilGenerator.Emit(OpCodes.Stind_R4);
                    return;
                }
                if (type == typeof(Double))
                {
                    ilGenerator.Emit(OpCodes.Stind_R8);
                    return;
                }
                ilGenerator.Emit(OpCodes.Stobj, type);
            }
            else
            {
                ilGenerator.Emit(OpCodes.Stind_Ref);
            }
        }
    }
}
